
# 从 src 核心公共模块导入所有内容
from src.Core.Common import *
# 从 src 核心接口模块导入 IEvaluationEngine 接口
from src.Core.Interfaces import IEvaluationEngine


class DefaultEvaluationEngine_Impl(IEvaluationEngine):
    """Default evaluation engine implementation
    该类实现了默认的评估引擎，用于处理节点的评估顺序和引脚数据获取
    """

    def __init__(self):
        """
        初始化 DefaultEvaluationEngine_Impl 类的实例，调用父类的构造函数
        """
        super(DefaultEvaluationEngine_Impl, self).__init__()

    @staticmethod
    def getPinData(pin):
        """
        获取引脚的数据。如果引脚没有连接，直接返回当前数据；
        否则，根据节点的可调用状态和评估顺序处理节点，然后返回数据。

        :param pin: 要获取数据的引脚对象
        :return: 引脚的数据
        """
        # 若引脚没有连接，直接返回当前数据
        if not pin.hasConnections():
            return pin.currentData()

        # 获取引脚所属节点的可调用标志
        bOwningNodeCallable = pin.owningNode().bCallable

        # 若节点不可调用，直接返回当前数据
        if not bOwningNodeCallable:
            return pin.currentData()

        # 获取引脚所属节点的评估顺序
        order = DefaultEvaluationEngine_Impl.getEvaluationOrderIterative(pin.owningNode())
        # 按评估顺序处理节点
        [node.processNode() for node in order]

        # 若节点不可调用，处理该节点
        if not bOwningNodeCallable:
            pin.owningNode().processNode()
        return pin.currentData()

    @staticmethod
    def TEST_getPinData(pin):
        """
        测试用的获取引脚数据的方法。如果引脚没有连接或不脏，直接返回当前数据；
        否则，根据节点的评估顺序处理节点，然后返回数据。

        :param pin: 要获取数据的引脚对象
        :return: 引脚的数据
        """
        # 若引脚没有连接，直接返回当前数据
        if not pin.hasConnections():
            return pin.currentData()

        # 获取引脚所属节点的可调用标志
        bOwningNodeCallable = pin.owningNode().bCallable

        # 若引脚不脏，直接返回当前数据
        if not pin.dirty:
            return pin.currentData()

        # 获取引脚所属节点的评估顺序
        order = DefaultEvaluationEngine_Impl.getEvaluationOrderIterative(pin.owningNode())
        # 按评估顺序处理节点
        [node.processNode() for node in order]

        # if pin.dirty:
        #    pin.owningNode().processNode()

        return pin.currentData()

    @staticmethod
    def getEvaluationOrderIterative(node, forward=False):
        """
        迭代地获取节点的评估顺序。使用栈来实现深度优先搜索。

        :param node: 起始节点
        :param forward: 是否向前搜索，默认为 False
        :return: 节点的评估顺序列表
        """
        # 存储已访问的节点
        visited = set()
        # 初始化栈，将起始节点放入栈中
        stack = [node]
        # 存储评估顺序的列表
        order = []
        while len(stack):
            # 取出栈顶节点
            node = stack[-1]
            stack.pop()

            # 若节点未被访问过，将其插入到评估顺序列表的开头，并标记为已访问
            if node not in visited:
                order.insert(0, node)
                visited.add(node)
            if not forward:
                # 若不向前搜索，获取节点的下一层节点
                lhsNodes = DefaultEvaluationEngine_Impl().getNextLayerNodes(node)
            else:
                # 若向前搜索，获取节点的前向的下一层节点
                lhsNodes = DefaultEvaluationEngine_Impl().getForwardNextLayerNodes(node)
            for n in lhsNodes:
                # 若节点未被访问过，将其加入栈中
                if n not in visited:
                    stack.append(n)
        # 移除最后一个节点
        order.pop()
        return order

    @staticmethod
    def getEvaluationOrder(node):
        """
        递归地获取节点的评估顺序。使用深度优先搜索。

        :param node: 起始节点
        :return: 节点的评估顺序列表
        """
        # 存储已访问的节点
        visited = set()
        # 存储评估顺序的列表
        order = []

        def dfsWalk(n):
            """
            深度优先搜索辅助函数

            :param n: 当前节点
            """
            # 标记当前节点为已访问
            visited.add(n)
            # 获取当前节点的下一层节点
            nextNodes = DefaultEvaluationEngine_Impl.getNextLayerNodes(n)
            for lhsNode in nextNodes:
                # 若节点未被访问过，递归调用 dfsWalk 函数
                if lhsNode not in visited:
                    dfsWalk(lhsNode)
            # 将当前节点添加到评估顺序列表中
            order.append(n)

        # 从起始节点开始深度优先搜索
        dfsWalk(node)
        # 移除最后一个节点
        order.pop()
        return order

    @staticmethod
    def getNextLayerNodes(node):
        """
        获取节点的下一层节点。根据节点的输入引脚和类型进行判断。

        :param node: 当前节点
        :return: 下一层节点的集合
        """
        # 存储下一层节点的集合
        nodes = set()
        # 获取节点的输入引脚
        nodeInputs = node.inputs

        # 若节点有输入引脚
        if not len(nodeInputs) == 0:
            for inputPin in nodeInputs.values():
                # 若输入引脚有受影响的引脚
                if not len(inputPin.affected_by) == 0:
                    # 检查是否为复合节点并深入处理
                    affectedByPins = set()
                    for pin in inputPin.affected_by:
                        if pin.owningNode().isCompoundNode:
                            # 若为复合节点，获取内部引脚
                            innerPin = pin.owningNode().outputsMap[pin]
                            affectedByPins.add(innerPin)
                        else:
                            affectedByPins.add(pin)

                    for outPin in affectedByPins:
                        # 获取输出引脚所属的节点
                        outPinNode = outPin.owningNode()
                        if not outPinNode.bCallable:
                            # 若节点不可调用，将其添加到下一层节点集合中
                            # if node.isDirty():
                            nodes.add(outPinNode)
        elif node.__class__.__name__ == "graphInputs":
            # 若节点为图输入节点
            for subgraphInputPin in node.outputs.values():
                for outPin in subgraphInputPin.affected_by:
                    # 获取输出引脚所属的节点并添加到下一层节点集合中
                    owningNode = outPin.owningNode()
                    nodes.add(owningNode)
        return nodes

    @staticmethod
    def getForwardNextLayerNodes(node):
        """
        获取节点前向的下一层节点。根据节点的输出引脚和类型进行判断。

        :param node: 当前节点
        :return: 前向下一层节点的集合
        """
        # 存储前向下一层节点的集合
        nodes = set()
        # 获取节点的输出引脚
        nodeOutputs = node.outputs

        # 若节点有输出引脚
        if not len(nodeOutputs) == 0:
            for outputPin in nodeOutputs.values():
                # 若输出引脚有影响的引脚
                if not len(outputPin.affects) == 0:
                    # 检查是否为复合节点并深入处理
                    affectedByPins = set()
                    for pin in outputPin.affects:
                        if pin.owningNode().isCompoundNode:
                            # 若为复合节点，获取内部引脚
                            innerPin = pin.owningNode().inputsMap[pin]
                            affectedByPins.add(innerPin)
                        else:
                            affectedByPins.add(pin)

                    for inPin in affectedByPins:
                        # 获取输入引脚所属的节点
                        inPinNode = inPin.owningNode()
                        if not inPinNode.bCallable:
                            # 若节点不可调用，将其添加到前向下一层节点集合中
                            nodes.add(inPinNode)
        elif node.__class__.__name__ == "graphOutputs":
            # 若节点为图输出节点
            for subgraphInputPin in node.inputs.values():
                for outPin in subgraphInputPin.affects:
                    # 获取输出引脚所属的节点并添加到前向下一层节点集合中
                    owningNode = outPin.owningNode()
                    nodes.add(owningNode)
        return nodes


@SingletonDecorator
class EvaluationEngine(object):
    """
    评估引擎单例类，使用单例装饰器确保只有一个实例。
    该类封装了默认评估引擎的实现，提供获取引脚数据的方法。
    """

    def __init__(self):
        """
        初始化 EvaluationEngine 类的实例，创建默认评估引擎的实现对象
        """
        self._impl = DefaultEvaluationEngine_Impl()

    def getPinData(self, pin):
        """
        调用默认评估引擎实现的 getPinData 方法获取引脚的数据

        :param pin: 要获取数据的引脚对象
        :return: 引脚的数据
        """
        return self._impl.getPinData(pin)